Custom Integrations
Custom integrations are a powerful feature in Downie that allows you to add custom handlers for sites that are not supported. It requires some knowledge of JavaScript which is used in context of the webpage.
When you add an integration, first you need to define regex for links that are handled by this integration. For example, if you for want to support links such as https://www.youtube.com/watch?v=MzPECgrwjKE, you would use a regex similar to https?://www\.youtube\.com/watch\?v=[\w_-]+. For more information about regex, please see e.g. https://en.wikipedia.org/wiki/Regular_expression but there are many other resources online.
Optionally, you can define an identifier within the regex using a named group called ID. Identifiers help Downie when looking for duplicate results in case it is extracting links from an unknown source. The identifier must not be empty and should uniquely identify the content it points to. In case of the YouTube example above, it’s the video ID that is the v query parameter value. To add the named group, surround the ID part with (?P<ID>id_regex) - i.e. https?://www\.youtube\.com/watch\?v=(?P<ID>[\w_-]+). Google for named regex groups for more information.
Once you have a regex filter in place, you need to define the JavaScript code. The code is loaded after a web view component loads the webpage - similar to when you write something into a console in the web inspector in Safari. What you do here is completely up to you.
For communicating with Downie, there are 3 methods:
1. Reporting a direct download
Use window.downie.reportDownload(download) to report to Downie that you’ve found a direct download. Direct download is a link that leads directly to a file (e.g. MP4) - unlike embedded content which would be e.g. a YouTube link (see below for more information).
The download must be a dictionary object with the following fields:
- this is the only required field and must be a valid URL. It is the link you want Downie to download. Starting Downie v4.3, this is deprecated in favor ofurlqualities.qualities- an array of downloads - see below for more information.subtitles- optionally, an array of subtitlestitle- name of the downloaddescription- optionally, descriptionpreview- image preview URLplaylistName- you can include playlist nameplaylistIndex- index within the playlist. The index starts at 1 (it’s not zero-based).playlistCount- if you know the number of downloads in the playlist, this is the numbershowName- name of the showauthors- list of authors (an array of strings)
1.1 Qualities
The field qualities should be an array of dictionaries defining different qualities provided by the site. The minimum required field is url which defines the URL to the download. Additional optional allowed fields are:
width- integer defining the width of this rendition.height- integer defining the height of this rendition.headers- a dictionary of key-value pairs that define HTTP headers that should be used for the download. The keys need to be the exact HTTP header field names. All values must be strings.disableChunkedDownloads- if set totrue(both boolean and string value is acceptable), then Downie will treat the download as if chunked downloads were disabled inPreferences > Advanced(i.e. making a single HTTP request for the entire file).audioQuality- if the rendition consists of separate audio and video streams, use this to provide an audio stream of the video. The value for this should be a dictionary that has the same fields as the video dictionary, i.e.url,headers,disableChunkedDownloads, but instead ofwidthandheightfields, it supports abitratefield. Note: requires Downie v4.5.2 or later.
Here is a very simple example:
var download = {
"qualities": [
{
"url": "https://www.example.com/file.mp4",
"width": 1024,
"height": 768,
"headers": {
"Referer": "https://www.example.com"
}
}
]
};
window.downie.reportDownload(download);
window.downie.reportDone();
1.2 Subtitles
The field subtitles of the download object, if populated, must contain an array of dictionaries and each dictionary must contain these two fields:
url- URL of the subtitles file.title- title of the subtitles - e.g. language. If there is no specific title, enter"subtitles"or something like that, but the title field is mandatory.
Example:
download.subtitles = [
{
"url": "https://www.example.com/subtitles.srt",
"title": "en"
}
]
2. Reporting an embedded download
Use window.downie.reportEmbeddedDownload(download) to report to Downie that you’ve found an embedded download, e.g. a YouTube or Vimeo link.
The download must be a dictionary object with the following fields:
url- this is the only required field and must be a valid URL. It is the link you want Downie to download.title- name of the download in case you want to override the titleplaylistName- you can include playlist nameplaylistIndex- index within the playlist. The index starts at 1 (it’s not zero-based).playlistCount- if you know the number of downloads in the playlist, this is the numbershowName- name of the showauthors- list of authors (an array of strings)context- optionally, you can pass a context to the embedded downloader. This can be any dictionary of strings - keys should be strings and values should be strings. If they are not strings, they will be “string-ified” automatically. This is useful in case you need to pass extra information to another custom integration. See 6. Using Context below.
3. Reporting you are done
This is very important - after you are done adding downloads, you should call window.downie.reportDone();. This tells Downie that you are done and it will process the downloads that you’ve reported. If you don’t do this, Downie will wait for a default timeout of 60 seconds before processing the results.
4. Reporting an error
Use window.downie.reportError("Something went wrong.") to report to Downie that the script failed to find what you were looking for. This causes Downie to close the web view and display the error in the UI.
5. Logging
Use window.downie.log("Something") to have Downie log something into the debug log. To enable the debug log, see the Debug menu in the menu bar.
6. Using Context
Since Downie v4.6.1, the window.downie object will contain a field context. This will be a dictionary that may contain the referer field (which contains the referring URL - e.g. if link from your custom integration gets extracted from http://www.example.com, then the referer field will contain this link).
Additionally, this can serve for passing information between integrations. If you have one integration that handles e.g. playlists and one that handles individual videos, you can pass information about the playlist to the integrations that handle individual videos:
// Playlist:
for (...) {
var download = { ... };
download.context = {
"playlist": "Some Playlist",
"index": 200,
"count": 400
};
downie.reportEmbeddedDownload(download);
}
// Individual video:
var playlistName = downie.context.playlist;
// ...
You are absolutely free to use any keys you like, this is fully up to you, but note that the referer field will be overridden by Downie.
Testing
You can open a Console window in Preferences > Custom Integrations and see the logged content (see above). Note that if the download fails, you should remove the download from the queue and add it again after modifying the source code. Retrying a download that’s already in the queue will run the original code.
Real-World Example
Here is a simple real-world example (and this example is actually usable on quite a few sites). More examples can be found in the GitHub repo for custom integrations.
Example URL: http://www-db.deis.unibo.it/courses/TW/DOCS/w3schools/html/html5_video.asp.html (let’s assume the page has a video on each webpage like this)
Integration Regex: https?://[^/]*unibo\.it/courses/.*/html/.*
This webpage uses HTML <video> tags and <source> tags inside, so you locate them using document.getElementsByTagName and extract the URL.
function getDownload() {
var elements = document.getElementsByTagName("source");
if (elements.length == 0) {
// No <source> tags found.
window.downie.reportError("No video elements.");
return null;
}
var url = elements[0].src;
if (url == null || url == "") {
// The source value is not loaded, or doesn't exist.
window.downie.reportError("No video URL in video element.");
return null;
}
var download = {
"qualities": [
{
"url": url
}
],
"title": "Example Download",
"preview": elements[0].parentElement.poster
};
return download;
}
var download = getDownload();
if (download != null) {
window.downie.reportDownload(download);
window.downie.reportDone();
}